(function (global, factory) {
	typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
		typeof define === 'function' && define.amd ? define(factory) :
			(global = global || self, global.daTools = factory());
}(this, function () {
	'use strict';
	var myModule = (function () {
		const modules = {};

		function define(name, depends, implement) {
			modules[name] = implement.apply(null, mapDepends(depends));
		}

		function mapDepends(depends) { // input: ['moduleA', 'moduleB' ...] output: [{method1, method2}, {method1, method2} ...] tips: one to one
			var i;
			if (!depends || !depends.length) return [];
			i = depends.length;
			while (i--) {
				depends[i] = modules[depends[i]];
			}
			return depends;
		}

		function get(name) {
			return modules[name];
		}

		return { define: define, get: get };
	})();

	myModule.define('types', [], function () { // 类型判断
		function isFunction(obj) {
			return Object.prototype.toString.call(obj) === "[object Function]";
		}
		function isNull(obj) {
			return Object.prototype.toString.call(obj) === "[object Null]";
		}
		function isNumber(obj) {
			return Object.prototype.toString.call(obj) === "[object Number]";
		}
		function isString(obj) {
			return Object.prototype.toString.call(obj) === "[object String]";
		}
		function isArray(obj) {
			return Object.prototype.toString.call(obj) === "[object Array]";
		}
		function isUndefined(obj) {
			return Object.prototype.toString.call(obj) === "[object Undefined]";
		}
		function isObject(obj) {
			return Object.prototype.toString.call(obj) === "[object Object]";
		}
		function isDate(obj) {
			return Object.prototype.toString.call(obj) === "[object Date]";
		}
		function isRegExp(obj) {
			return Object.prototype.toString.call(obj) === "[object RegExp]";
		}
		function isMath(obj) {
			return Object.prototype.toString.call(obj) === "[object Math]";
		}

		function isPhone(str) {
			var reg = /^[1][3,4,5,7,8,9][0-9]{9}$/;
			return reg.test(str);
		}

		function isEmail(str) {
			var reg = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/;
			return reg.test(str);
		}

		function isIdCard(str) {
			var reg = /^\d{15}|\d{18}$/;
			return reg.test(str);
		}

		function isValid(str) {
			var reg = /^[a-zA-Z][a-zA-Z0-9_]{4,15}$/;
			return reg.test(str);
		}

		function isCnChar(str) {
			var reg = /[\u4e00-\u9fa5]/;
			return reg.test(str);
		}

		function isWeixin() {
			return window.navigator.userAgent.toLowerCase().indexOf('micromessenger') != -1;
		}


		return {
			isFunction: isFunction,
			isNull: isNull,
			isNumber: isNumber,
			isString: isString,
			isArray: isArray,
			isUndefined: isUndefined,
			isObject: isObject,
			isDate: isDate,
			isRegExp: isRegExp,
			isMath: isMath,
			isEmail: isEmail,
			isIdCard: isIdCard,
			isCnChar: isCnChar,
			isValid: isValid,
			isPhone: isPhone,
			isWeixin: isWeixin
		};
	});

	myModule.define('delay', ["types"], function (types) {  // 延迟做一些事

		function throttle(method, { context = null, time = 500, options = [] }) {
			method.tId && clearTimeout(method.tId);
			method.tId = setTimeout(function () {
				method.apply(context, options);
			}, time);
		}

		return { throttle: throttle };

	});

	myModule.define('requestKey', ["types"], function (types) {  // http request : query key  add key http request completed: remove key

		// requestList: { key: true}
		var requestList = {} //api请求记录
		// 将当前请求的api记录起来

		function add(key) {

			requestList[key] = true

		}



		// 将请求完成的api从记录中移除

		function remove(key) {

			delete requestList[key]

		}



		//当前请求的api是否已有记录

		function has(key) {

			return requestList[key]

		}

		function get(data) {

			if (!types.isObject(data)) return data;

			var ajaxKey = JSON.stringify(data);

			return ajaxKey;

		}

		return { add: add, remove: remove, has: has, get: get };

	});

	myModule.define('myString', ['types'], function (types) {  // 

		function preFixAny(str, size = 2, char = 0) {
			if (String(str).length >= size) return str;
			return (Array(+size).join(char) + str).slice(-size);
		}


		function parseQuery(url) { // 把url参数转换为对象
			if (!url || !types.isString(url) || !url.split('#')[0].split('?')[1]) return {};

			var str = url.split('#')[0].split('?')[1];
			var result = {};
			var temp = str.split('&');
			for (var i = 0; i < temp.length; i++) {
				var temp2 = temp[i].split('=');
				result[temp2[0]] = temp2[1];
			}

			return result;
		}

		function joinQuery(url, queryObj) { //拼接url参数 已有参数要避免重复
			var options, temps, opt, i, str, tempsStr;
			if (!queryObj || !types.isString(url) || !types.isObject(queryObj) || JSON.stringify(queryObj) === '{}') return url;
			// 整合参数
			options = parseQuery(url);
			for (opt in queryObj) {
				options[opt] = queryObj[opt];
			}
			console.log(options);
			// 拼接参数
			temps = [];
			for (i in options) {
				str = i + "=" + options[i];
			}
			tempsStr = temps.join("&");
			// 生成链接
			url = url.split('?')[0] + '?' + tempsStr + (/#/.test(url) ? '#' + url.split('#')[1] : '');
			return url;
		}


		function delQueryOption(url, delArr = []) {
			var options = parseQuery(url);
			var resUrl = url.split('#')[0].split('?')[0];
			for (var key in options) {
				if (delArr.indexOf(key) < 0) {
					resUrl += /\?/.test(resUrl) ? '&' + key + '=' + options[key] : '?' + key + '=' + options[key]
				}
			}
			resUrl += /#/.test(url) ? '#' + url.split('#')[1] : '';
			return resUrl

		}
		function utf16toEntities(str) { 
			var patt=/[\ud800-\udbff][\udc00-\udfff]/g; // 检测utf16字符正则 
			str = str.replace(patt, function(char){ 
					var H, L, code; 
					if (char.length===2) { 
						H = char.charCodeAt(0); // 取出高位 
						L = char.charCodeAt(1); // 取出低位 
						code = (H - 0xD800) * 0x400 + 0x10000 + L - 0xDC00; // 转换算法 
						return "&#" + code + ";"; 
					} else { 
						return char; 
					} 
				}); 
			return str; 
		}

		/**
		 *
		 *
		 *用于反解开EMOJI编码后的字符串
		*
		*
		* */
		function uncodeUtf16(str){
			var reg = /\&#.*?;/g;
			var result = str.replace(reg,function(char){
				var H,L,code;
				if(char.length == 9 ){
					code = parseInt(char.match(/[0-9]+/g));
					H = Math.floor((code-0x10000) / 0x400)+0xD800;
					L = (code - 0x10000) % 0x400 + 0xDC00;
					return unescape("%u"+H.toString(16)+"%u"+L.toString(16));
				}else{
					return char;
				}
			});
			return result;
		}


		function getBytes(str) {
			let len = str.length, bytes = len, charCode = -1;
			for (let i = 0; i < len; i++) {
				charCode = str.charCodeAt(i);
				if (charCode > 255)
					bytes++
			}
			return bytes;
		}

		function splitByLen(str, length, separator) {
			var arr = null;
			if (getBytes(str) <= length && separator) {   //如果本身长度就不够，则返回本身的数组
				arr = str.split(separator);
				return arr;
			} else if (getBytes(str) <= length && !separator) {
				return [str];
			}

			// 以下都是字符的长度，大于指定的长度

			if (separator) {   //如果指定了分隔符
				arr = str.split(separator);
			} else { //没有指定，则把回车替换为空格
				str = str.replace(/\n/g, ' ');
				arr = [str];
			}


			let resArr = [];  // 结果分割数组
			arr.forEach((v, i) => {
				if (getBytes(str) <= length) {
					resArr.push(v);
				} else {
					let charCode = -1, value, curLen = 0, len = v.length, startIndex = 0;
					for (let i = 0; i < len; i++) {
						charCode = v.charCodeAt(i);
						if (charCode >= 0 && charCode <= 255)
							curLen++;
						else
							curLen += 2;

						if (curLen > length) {
							value = v.slice(startIndex, i);
							resArr.push(value);
							startIndex = i;
							charCode > 255 ? (curLen = 2) : (curLen = 1);
						}

						if (i == len - 1 && curLen <= length) {   //循环到最后一个了, 并且没有超过最大的长度时
							value = v.slice(startIndex, i + 1);
							resArr.push(value);
						}
					}
				}
			});

			return resArr;
		}



		return {
			getBytes: getBytes,
			splitByLen: splitByLen,
			joinQuery: joinQuery,
			parseQuery: parseQuery,
			delQueryOption: delQueryOption,
			preFixAny: preFixAny,
			utf16toEntities: utf16toEntities,
			uncodeUtf16: uncodeUtf16
		}
	});

	myModule.define('cDate', ['types', 'myString'], function (types, myString) {
		function getWhichMonth(date) {
			if (!types.isDate(date)) return console.error('只能接收date对象，而接收的是：%s' + date);
			var curDate = new Date();
			var differYear = date.getFullYear() - curDate.getFullYear();
			var differMonthsSingle = date.getMonth() - curDate.getMonth();
			var whichMonth = differYear * 12 + differMonthsSingle;

			return whichMonth;
		}

		function getWhichWeek(date) {
			if (!types.isDate(date)) return console.error('只能接收date对象，而接收的是：%s' + date);
			// 计算出距离当前时间的星期一相差的天数
			var curDate = new Date();
			var curMondayTimeStamp = curDate.setDate(curDate.getDate() - curDate.getDay());
			var differDays = Math.ceil((+date - curMondayTimeStamp) / 1000 / 60 / 60 / 24);
			var whichWeek = Math.ceil(differDays / 7) - 1;

			return whichWeek;

		}

		function getWhichDay(date) {
			if (!types.isDate(date)) return console.error('只能接收date对象，而接收的是：%s' + date);
			var curDate = new Date();
			var _date = new Date(`${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`);

			var _curDate = new Date(`${curDate.getFullYear()}/${curDate.getMonth() + 1}/${curDate.getDate()}`);
			var whichDay = Math.ceil((+_date - _curDate) / 1000 / 60 / 60 / 24);

			return whichDay;
		}

		function getScopeOfMonth(whichMonth) {
			var curDate = new Date();
			var start = new Date(curDate.getFullYear(), curDate.getMonth() + whichMonth, 1).getTime();
			var end = new Date(curDate.getFullYear(), curDate.getMonth() + 1 + whichMonth, 0).getTime();
			return { start, end }
		}

		function formatTime(date, type = 'date') {

			const year = date.getFullYear()
			const month = date.getMonth() + 1
			const day = date.getDate()

			const hour = date.getHours()
			const minute = date.getMinutes()
			const second = date.getSeconds()
			const t1 = [year, month, day].map((item) => myString.preFixAny(item)).join('/')
			const t2 = [hour, minute, second].map((item) => myString.preFixAny(item)).join(':')
			return type === 'date' ? `${t1}` : `${t1} ${t2}`;
		}

		return { getWhichMonth, getWhichWeek, getWhichDay, formatTime };
	});

	myModule.define('mpCanvas', ['myString', 'types'], function (myString, types) {
		// 功能： 1. 渲染canvas， draw（）。 2. 生成图片或base64
		var canvasWidth, canvasHeight;
		// Only entrance
		async function render(width, height, data, canvasId = 'canvas') {
			// console.log('render');
			// checkout
			if (!this) return console.error('invoking render without canvasContext');
			if (!data || !types.isArray(data)) return console.error('data muse have and data muse be Array type');
			canvasWidth = width; canvasHeight = height;
			// Start distributing
			data.forEach(dataItem => {

				switch (dataItem.type) {

					case 'image':
						// console.log('image');
						console.log(dataItem.props.src);
						_renderImageByStyle.call(this, dataItem.props.src, dataItem.style);

						break;

					case 'text':
						// console.log('text');  
						_renderTextByStyle.call(this, dataItem.text, dataItem.style);

						break;

					case 'rect':
						// console.log('rect');  
						_renderRectByStyle.call(this, dataItem.style);

						break;

					case 'clearRect':
						// console.log('clearRect');
						_clearRect.call(this, dataItem.style.left, dataItem.style.top, dataItem.style.width, dataItem.style.height);

						break;
				}

			});

			this.draw();  // Start drawing
			return await _getCanvasTempFilePath.call(this, canvasId);
		}

		// private method

		async function _renderImageByStyle(src, { width, height, left = 0, top = 0 }) { // 使用style画图片

			if (!src || !src.trim()) return;
			width === '100%' && (width = canvasWidth), height === '100%' && (height = canvasHeight);
			_renderImage.call(this, src, left, top, width, height);
		}

		function _renderTextByStyle(text, { left, top, fontSize, color, width, rowSpacing }) { // 使用style画文字
			var len, rowText, arrText, rowTop = top;

			fontSize && (this.font = fontSize + 'px "microsoft yahei",Helvetica,Arial');
			color && (this.fillStyle = color);

			if (width) { // 对文字进行换行处理

				len = width / fontSize * 2;
				arrText = myString.splitByLen(text, len);
				fontSize -= 4;
				while (arrText.length > 0) {
					rowText = arrText.shift();
					rowTop = rowTop + (rowTop === top ? fontSize : rowSpacing + fontSize);

					_renderText.call(this, rowText, left, rowTop);
				}

			} else { // 单行处理
				fontSize -= 4;
				top = top + fontSize;
				_renderText.call(this, text, left, top);
			}



		}

		function _renderRectByStyle({ width, height, left, top, backgroundColor, borderRadius = 0, borderWidth, borderColor }) { // 使用style画矩形，圆角矩形，圆
			width === '100%' && (width = canvasWidth), height === '100%' && (height = canvasHeight);
			if (+width === 0 || +height === 0) return;
			if (borderRadius === '50%') { // 画圆
				if (backgroundColor) {

					_renderCircle.call(this, left + width / 2, top + width / 2, width / 2, true, backgroundColor);
				}

				if (borderWidth) {
					_renderCircle.call(this, left + width / 2, top + width / 2, width / 2, false, borderColor);
				}

			} else if (borderRadius) { // 画圆角矩形

				if (backgroundColor) {

					_renderRoundRect.call(this, left, top, width, height, borderRadius, true, backgroundColor);
				}

				if (borderWidth) {
					_renderRoundRect.call(this, left, top, width, height, borderRadius, false, borderColor, borderWidth);
				}

			} else { // 画直角矩形

				if (backgroundColor) {

					_renderRect.call(this, left, top, width, height, true, backgroundColor);
				}

				if (borderWidth) {
					_renderRect.call(this, left, top, width, height, false, borderColor, borderWidth);
				}

			}
		}

		function _renderRect(x, y, w, h, isFill = true, color, lineWidth) { // 画直角矩形
			lineWidth && (this.lineWidth = lineWidth);
			color && (isFill ? this.fillStyle = color : this.strokeStyle = color);
			isFill ? this.fillRect(x, y, w, h) : this.strokeRect(x, y, w, h);
		}


		function _renderRoundRect(x, y, w, h, r, isFill = true, color, lineWidth) { // 画圆角矩形
			// 开始绘制
			lineWidth && (this.lineWidth = lineWidth);
			r = parseInt(r);
			this.beginPath();
			// 因为边缘描边存在锯齿，最好指定使用 transparent 填充
			// 这里是使用 fill 还是 stroke都可以，二选一即可
			color && (isFill ? this.fillStyle = color : this.strokeStyle = color);
			// 左上角
			this.arc(x + r, y + r, r, Math.PI, Math.PI * 1.5);

			// border-top
			this.moveTo(x + r, y);
			this.lineTo(x + w - r, y);
			//this.lineTo(x + w, y + r);
			// 右上角
			this.arc(x + w - r, y + r, r, Math.PI * 1.5, Math.PI * 2);

			// border-right
			this.lineTo(x + w, y + h - r);
			//this.lineTo(x + w - r, y + h);
			// 右下角
			this.arc(x + w - r, y + h - r, r, 0, Math.PI * 0.5);

			// border-bottom
			this.lineTo(x + r, y + h);
			//this.lineTo(x, y + h - r);
			// 左下角
			this.arc(x + r, y + h - r, r, Math.PI * 0.5, Math.PI);

			// border-left
			this.lineTo(x, y + r);
			//this.lineTo(x + r, y);

			// 这里是使用 fill 还是 stroke都可以，二选一即可，但是需要与上面对应
			isFill ? this.fill() : this.stroke();
			this.closePath();
			// 剪切
			//this.clip();
		}

		function _renderCircle(x, y, r, isFill = true, color, lineWidth) { // 画圆
			lineWidth && (this.lineWidth = lineWidth);
			color && (isFill ? this.fillStyle = color : this.strokeStyle = color);
			this.arc(x, y, r, 0, 2 * Math.PI);
			isFill ? this.fill() : this.stroke();
		}

		function _renderImage(src, x, y, w, h) { // 画图片
			this.drawImage(src, x, y, w, h);
		}

		function _renderText(text, x, y) { // 画文字
			this.fillText(text, x, y);
		}

		function _clearRect(x, y, w, h) {
			this.clearRect(x, y, w, h);
		}

		function _getCanvasTempFilePath(canvasId) { // 获取canvas输出的临时路径
			return new Promise(resolve => {
				setTimeout(() => {
					try {
						uni.canvasToTempFilePath({
							canvasId,
							destWidth: canvasWidth,
							destHeight: canvasHeight,
							success(res) {
								resolve(res.tempFilePath);
							},

							fail() {
								resolve("");
							}
						});
					} catch (err) {
						mpvue.canvasToTempFilePath({
							canvasId,
							destWidth: canvasWidth,
							destHeight: canvasHeight,
							success(res) {
								resolve(res.tempFilePath);
							},

							fail() {
								resolve("");
							}
						});
					}



				}, 600);

			});
		}

		// output
		return { render: render };
	});

	myModule.define('event', ['types'], function (types) {
		// Register Event Listener
		var events = {};
		function on(type, self, handler, options = [], isOnce = true) {
			var curQueues = events[type];
			var event = {
				self: self,
				handler: handler,
				options: options,
				isOnce: isOnce
			};
			if (curQueues && types.isArray(curQueues)) {
				for (var i = 0; i < curQueues.length; i++) {
					if (curQueues[handler] === self && JSON.stringify(handler) === JSON.stringify(curQueues[handler])) return
				}
				curQueues.push(event);
			} else {
				events[type] = [event];
			}
		}
		// emit event
		function emit(type, data) {
			var curQueues, event;
			curQueues = events[type];
			if (curQueues && curQueues.length) {
				var i = curQueues.length;
				while (i--) {
					event = curQueues[i];
					event.handler.apply(event.self, [...event.options, data]);
					setTimeout(() => {
						event.isOnce && remove(type, event.self, event.handler);
					}, 1000);

				}
			}

		}

		// remove event listener
		function remove(type, self, handler) {
			var curQueues, event, i;
			curQueues = events[type];

			if (curQueues && curQueues.length) {
				i = curQueues.length;
				while (i--) {
					event = curQueues[i];
					if (event.self === self && JSON.stringify(event.handler) === JSON.stringify(handler)) {
						curQueues.splice(i, 1);
					}
				}
			}

		}

		return { on: on, emit: emit, remove: remove };
	});

	myModule.define('wxShare', ['types'], function (types) {
		function checkout(wx, options, debug) {
			if (!wx || !types.isObject(wx)) return new Error('请先引入wx JSSDK,例如：http://res.wx.qq.com/open/js/jweixin-1.4.0.js');
			if (!options || !types.isObject(options)) return new Error('期望传入配置对象，而传入的是' + options);
			wx.config({
				debug: debug || false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来，若要查看传入的参数，可以在pc端打开，参数信息会通过log打出，仅在pc端时才会打印。
				appId: options.appId, // 必填，公众号的唯一标识
				timestamp: options.timestamp, // 必填，生成签名的时间戳
				nonceStr: options.nonceStr || options.noncestr, // 必填，生成签名的随机串
				signature: options.signature, // 必填，签名
				jsApiList: ['updateAppMessageShareData', 'updateTimelineShareData'] // 必填，需要使用的JS接口列表
			});
		}

		function call(wx, title, desc, imgUrl, link) {
			if (!wx || typeof wx !== 'object') return new Error('请先引入wx JSSDK,例如：http://res.wx.qq.com/open/js/jweixin-1.4.0.js');
			if (!title) console.error('没有传入分享的标题');
			if (!desc) console.error('没有传入分享的内容');
			if (!imgUrl) console.error('没有传入分享的图片');
			var link = link || window.location.href;
			wx.ready(function () {
				wx.updateAppMessageShareData({
					title: title, // 分享标题
					desc: desc, // 分享描述
					link: link, // 分享链接，该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
					imgUrl: imgUrl, // 分享图标
					success: function (res) {
						// 设置成功
					},
					fail(err) {
						console.log(err);
					}
				});
				wx.updateTimelineShareData({
					title: title, // 分享标题
					link: link, // 分享链接，该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
					imgUrl: imgUrl, // 分享图标
					success: function (res) {
						// 设置成功
						//console.log(res);
					},
					fail(err) {
						console.log(err);
					}
				})
			});
		}

		return { checkout: checkout, call: call }

	});
	return myModule;
}));
